Come controllare la porta parallela da qualunque sistema Operativo
Il controllo delle porte I/O è un problema delicato, ove il software e l’hardware arrivano a sfiorarsi. È possibile inviare al PC istruzioni maldestre che compromettano il funzionamento della macchina. Per scongiurare eventuali problemi i sistemi operativi della Microsoft dopo NT4 hanno preso il controllo completo delle porte I/O. Le istruzioni di lettura e scrittura come port nel PASCAL e inp nel C generano un errore istruzione privilegiata terminando il flusso del programma. Per questo motivo negli articoli di elettronica applicata pubblicati da questa rivista si specifica si utilizzare come sistema operativo Windows 98 o 95.
(Vedi immagine nel file Html).
Il problema si risolve utilizzando un driver. Ad esempio si può sfruttare PortTalk Driver for Windows NT/2000/XP che si scarica da http://www.beyondlogic.org. Questo driver funziona correttamente con i sistemi Windows NT/2000/XP ( in allegato trovate il file porttalk22.zip con la documentazione ufficiale)
Rimane il problema di non conoscere a priori il sistema operativo della macchina. Se abbiamo un sistema operativo fino a Windows ME dobbiamo usare i comandi inp(), altrimenti dobbiamo ricorrere al Driver.
Riconoscere il sistema operativo
Per risolvere questo problema ho scritto una classe Cport  oltre incorporare il codice di gestione del driver va a riconoscere il sistema operativo. Questa operazione è svolta dalla seguente funzione

BOOL CPort::GetOpSystem()
{
     OSVERSIONINFO *osvi;
     osvi = new(OSVERSIONINFO);
     osvi->dwOSVersionInfoSize=sizeof(OSVERSIONINFO) ;
     GetVersionEx (osvi);
     bIsWindowsNTorLater = (osvi->dwPlatformId == VER_PLATFORM_WIN32_NT );
     try {delete osvi;}
     catch(...){;};
     return bIsWindowsNTorLater;
}
per cui prima di leggere/scrivere il valore della porta si controlla il tipo di sistema operativo

unsigned char CPort::inport(unsigned short PortAddress)
{
     if (!bIsWindowsNTorLater)
     {
          return _inp(PortAddress);
     };
     unsigned int error;
     DWORD BytesReturned;
     unsigned char Buffer[3];
     unsigned short * pBuffer;
     pBuffer = (unsigned short *)&Buffer;
     *pBuffer = PortAddress;
     error = DeviceIoControl(PortTalk_Handle,
     IOCTL_READ_PORT_UCHAR,
     &Buffer,
     2,
     &Buffer,
     1,
     &BytesReturned,
     NULL);
     if (!error)
     AfxMessageBox("Errore di comunicazione con il driver",MB_OK,0);
     return(Buffer[0]);
}
void CPort::outport(unsigned short PortAddress, unsigned char byte)
{
     if (!bIsWindowsNTorLater)
     {
          _outp(PortAddress,byte);
          return;
     };
     unsigned int error;
     DWORD BytesReturned; 
     unsigned char Buffer[3];
     unsigned short * pBuffer;
     pBuffer = (unsigned short *)&Buffer[0];
     *pBuffer = PortAddress;
     Buffer[2] = byte;
     error = DeviceIoControl(PortTalk_Handle,
     IOCTL_WRITE_PORT_UCHAR,
     &Buffer,
     3,
     NULL,
     0,
     &BytesReturned,
     NULL);
     if (!error) 
     AfxMessageBox("Errore di comunicazione con il driver",MB_OK,0);
}

L’utilizzo della classe è molto semplice. Bisogna dichiarare una variabile di tipo Cport e assegnare il corretto valore all’indirizzo. A questo punto l’operazione di lettura è svolta attraverso il metodo leggi che aggiorna le proprietà value (valore decimale della variabile), e bit[i] (array contenente i singoli bit). L’operazione di scrittura avviene sul singolo bit attraverso il metodo setout(unsigned int value, unsigned char bitnum).
Per funzionare correttamente insieme alla calsse Cport è necessario includere nella cartella dell’eseguibile anche il file porttalk.sys (il vero driver).

Tutto così semplice?
No. Installare un servizio o un driver in Windows NT o 2000 o XP non è cosa da tutti! O meglio bisogna avere i diritti di amministratori. Questo vale solo per la prima volta. Basta allora far eseguire una volta il programma dall’amministratore e poi avete il controllo delle porte I/O.

In allegato trovate un progetto per il controllo della porta parallela equivalente a quello pubblicato su ioProgrammo n.57-58, ma funzionante con tutti i sistemi operativi.
Tip fornito dal Sig. F. Gobbi

Come sospendere l’esecuzione di un programma

A volte è necessario sospendere l’esecuzione di un’applicazione per un determinato periodo di tempo. La funzione “pausa” scritta in C+, ed oggetto di questo tip, fa proprio al caso nostro: sospende, infatti, l’esecuzione di un programma per un intervallo di tempo espresso in secondi. Di seguito viene riportato il listato corredato da un esempio di utilizzo.
Tip fornito dal Sig. R.Pizzolante

#include <iostream.h>
#include <time.h>

/*funzione pausa*/
void pausa(double sec)
{
     clock_t t = (clock_t)(clock() + (CLOCKS_PER_SEC * sec));
     while (clock() < t)
     ;
     return;
}

/*Esempio di utilizzo :
stampa la stringa puntata da s con effetto “macchina da scrivere” */

int main(void)
{
     char *s = "Raffaele Pizzolante";
     for ( ; *s; s++) {
     cout << *s;
     pausa(1.0 / 5.0); /*1/5 di secondo*/
     
return 0;
}

Come monitorare i processi di Windows

Questo tip mostra come sia possibile monitorare i processi di Windows con un'applicazione C#. In particolare si possono ottenere dettagliate informazioni riguardanti l'ID del processo, la memoria utilizzata, il modulo principale, etc.; La classe "Process" del Namespace "System.Diagnostic" consente, inoltre, di inviare un segnale "Kill" ai processi attivi.
Tip fornito dal Sig D.Camassa

private void startMonitor()
{
     while(true)
     {
     //Per l'aggiornamento dell'albero è necessario utilizzare un delegato
     ProcessTree.BeginInvoke(new UpdateTree(UpdateTreeDelegate));
     //Refresh ogni 10 secondi
     thProcess.Join(5000);
     }
}
private void UpdateTreeDelegate()
{
     ProcessTree.Nodes[0].Nodes.Clear();
     ProcessTree.BeginUpdate();
     //ottengo un array dei processi attivi
     process=System.Diagnostics.Process.GetProcesses();
     //costruisco l'albero dei processi
     for(int i=0;i<process.Length;i++)
     {
          ProcessTree.Nodes[0].Nodes.Add(new 
          TreeNode(process[i].ProcessName,1,1));
     }

     ProcessTree.EndUpdate();
     //Riseleziono l'ultimo nodo scelto prima del refresh
     ProcessTree.SelectedNode=ProcessTree.Nodes[0].Nodes[SelectedNodeIndex];
}
private void ProcessTree_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
{
     if(!(((TreeView)sender).SelectedNode.Text.Equals("Processi")))
     {
          try
          {
               SelectedNodeIndex=ProcessTree.SelectedNode.Index;
               Process pr=Process.GetProcessById(process[((TreeView)sender).
               SelectedNode.Index].Id);
               StringBuilder sb=new StringBuilder();
               //Ottengo tutte le informazioni sul processo selezionato
               sb.Append("Name:"+pr.ProcessName+"\n");
               sb.Append("ID:"+pr.Id+"\n");
               sb.Append("Priorità:"+pr.BasePriority+"\n");
               sb.Append("Handle:"+pr.Handle +"\n");
               sb.Append("Modulo principale:"+pr.MainModule+"\n");
               sb.Append("Dimensione della memoria 
                      privata:"+pr.PrivateMemorySize +"\n");
               sb.Append("Avvio:"+pr.StartTime +"\n");
               sb.Append("thread:"+pr.Threads+"\n");
               sb.Append("Tempo utente di processore:"+pr.UserProcessorTime 
               +"\n");
               sb.Append("Memoria virtuale usata:"+pr.VirtualMemorySize+"\n");
               InformationProcess.Text=sb.ToString();
          }
          catch(Exception err)
          {
               InformationProcess.Text="Errore nella lettura delle informazioni: 
               "+err.Message;
          }
     }
}

private void menuItem1_Click(object sender, System.EventArgs e)
{
     //ATTENZIONE:Uccido il processo selezionato
     try
     {
          process[ProcessTree.SelectedNode.Index].Kill();
          ProcessTree.SelectedNode=ProcessTree.Nodes[0].Nodes[0];
     }
     catch(Exception er){MessageBox.Show("Errore: "+er.Message);}
}